x86: softtsc for PV domains
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 27 Aug 2009 10:25:34 +0000 (11:25 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 27 Aug 2009 10:25:34 +0000 (11:25 +0100)
Implement softtsc (TSC emulation) for userland code in PV domains.  It
currently is tied to the existing "softtsc" Xen boot option (which
does the same thing but for HVM domains).  Later it should be tied to
a vm.cfg option, but this is sufficient for now to obtain performance
degradation data for PV environments that heavily utilize rdtsc.  To
record emulation frequency, use debug-key "s".

Signed-off-by: Dan Magenheimer <dan.magenheimer@oracle.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/arch/x86/domain.c
xen/arch/x86/hvm/hvm.c
xen/arch/x86/time.c
xen/arch/x86/traps.c
xen/include/asm-x86/domain.h
xen/include/asm-x86/hvm/support.h
xen/include/asm-x86/time.h

index d0eaba2f8ff6a62aaf5b49ff10ec4c83f9b27f90..94819a675e1dbe2b0273db4d9bb32283c5e46d45 100644 (file)
@@ -516,6 +516,10 @@ int arch_domain_create(struct domain *d, unsigned int domcr_flags)
         d->arch.cpuids[i].input[1] = XEN_CPUID_INPUT_UNUSED;
     }
 
+    /* For now, per-domain SoftTSC status is taken from global boot param. */
+    d->arch.vtsc = opt_softtsc;
+    spin_lock_init(&d->arch.vtsc_lock);
+
     return 0;
 
  fail:
@@ -1259,7 +1263,7 @@ static void paravirt_ctxt_switch_to(struct vcpu *v)
     set_int80_direct_trap(v);
     switch_kernel_stack(v);
 
-    cr4 = pv_guest_cr4_to_real_cr4(v->arch.guest_context.ctrlreg[4]);
+    cr4 = pv_guest_cr4_to_real_cr4(v);
     if ( unlikely(cr4 != read_cr4()) )
         write_cr4(cr4);
 
index 15d2e32da53a93a24aa463e747deb59d3dc90e72..923987214ce8f1ce129859a7e085bbc3ea21cd26 100644 (file)
@@ -61,9 +61,6 @@ int hvm_enabled __read_mostly;
 unsigned int opt_hvm_debug_level __read_mostly;
 integer_param("hvm_debug", opt_hvm_debug_level);
 
-int opt_softtsc;
-boolean_param("softtsc", opt_softtsc);
-
 struct hvm_function_table hvm_funcs __read_mostly;
 
 /* I/O permission bitmap is globally shared by all HVM guests. */
index 8fc2a4836b16cd2685c10975bf165c20a2406e70..0fb2aaa29f1406c8be4b2c31e672fe3938b9abff 100644 (file)
@@ -21,6 +21,7 @@
 #include <xen/smp.h>
 #include <xen/irq.h>
 #include <xen/softirq.h>
+#include <xen/keyhandler.h>
 #include <asm/io.h>
 #include <asm/msr.h>
 #include <asm/mpspec.h>
@@ -35,6 +36,9 @@
 static char opt_clocksource[10];
 string_param("clocksource", opt_clocksource);
 
+int opt_softtsc;
+boolean_param("softtsc", opt_softtsc);
+
 /*
  * opt_consistent_tscs: All TSCs tick at the exact same rate, allowing
  * simplified system time handling.
@@ -1429,6 +1433,57 @@ struct tm wallclock_time(void)
     return gmtime(seconds);
 }
 
+
+/*
+ * PV SoftTSC Emulation.
+ */
+
+static unsigned long rdtsc_kerncount, rdtsc_usercount;
+
+void pv_soft_rdtsc(struct vcpu *v, struct cpu_user_regs *regs)
+{
+    s_time_t now;
+
+    if ( guest_kernel_mode(v, regs) )
+    {
+        rdtsc_kerncount++;
+        rdtsc(regs->eax, regs->edx);
+    }
+    else
+    { 
+        rdtsc_usercount++;
+        spin_lock(&v->domain->arch.vtsc_lock);
+        now = get_s_time() + v->domain->arch.vtsc_stime_offset;
+        if ( (int64_t)(now - v->domain->arch.vtsc_last) >= 0 )
+            v->domain->arch.vtsc_last = now;
+        else
+            now = v->domain->arch.vtsc_last;
+        spin_unlock(&v->domain->arch.vtsc_lock);
+        regs->eax = (uint32_t)now;
+        regs->edx = (uint32_t)(now >> 32);
+    }
+}
+
+static void dump_softtsc(unsigned char key)
+{
+    printk("softtsc count: %lu kernel, %lu user\n",
+           rdtsc_kerncount, rdtsc_usercount);
+}
+
+static struct keyhandler dump_softtsc_keyhandler = {
+    .diagnostic = 1,
+    .u.fn = dump_softtsc,
+    .desc = "dump softtsc stats"
+};
+
+static int __init setup_dump_softtsc(void)
+{
+    if ( opt_softtsc )
+        register_keyhandler('s', &dump_softtsc_keyhandler);
+    return 0;
+}
+__initcall(setup_dump_softtsc);
+
 /*
  * Local variables:
  * mode: C
index 61e3407f0e5cfc828ec01b50d70e2f944c42147f..6feb8ccdbd90414937aba3f58e3e0737ccc34577 100644 (file)
@@ -2005,12 +2005,15 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
     goto fail;
 
  twobyte_opcode:
-    /* Two-byte opcodes only emulated from guest kernel. */
-    if ( !guest_kernel_mode(v, regs) )
+    /*
+     * All two-byte opcodes, except RDTSC (0x31) are executable only from
+     * guest kernel mode (virtual ring 0).
+     */
+    opcode = insn_fetch(u8, code_base, eip, code_limit);
+    if ( !guest_kernel_mode(v, regs) &&
+         !((opcode == 0x31) && v->domain->arch.vtsc) )
         goto fail;
 
-    /* Privileged (ring 0) instructions. */
-    opcode = insn_fetch(u8, code_base, eip, code_limit);
     if ( lock && (opcode & ~3) != 0x20 )
         goto fail;
     switch ( opcode )
@@ -2127,8 +2130,7 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
 
         case 4: /* Write CR4 */
             v->arch.guest_context.ctrlreg[4] = pv_guest_cr4_fixup(*reg);
-            write_cr4(pv_guest_cr4_to_real_cr4(
-                v->arch.guest_context.ctrlreg[4]));
+            write_cr4(pv_guest_cr4_to_real_cr4(v));
             break;
 
         default:
@@ -2266,7 +2268,7 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
     }
 
     case 0x31: /* RDTSC */
-        rdtsc(regs->eax, regs->edx);
+        pv_soft_rdtsc(v, regs);
         break;
 
     case 0x32: /* RDMSR */
index 8e6f0aba57d2e1b4b097dd552c80b498e8fe5809..cf65772ebb280089e51e505d0fc5a8cd7188721b 100644 (file)
@@ -299,6 +299,12 @@ struct arch_domain
 
     /* For Guest vMCA handling */
     struct domain_mca_msrs vmca_msrs;
+
+    /* SoftTSC emulation */
+    bool_t vtsc;
+    s_time_t vtsc_last;
+    spinlock_t vtsc_lock;
+    int64_t vtsc_stime_offset;
 } __cacheline_aligned;
 
 #define has_arch_pdevs(d)    (!list_empty(&(d)->arch.pdev_list))
@@ -426,10 +432,13 @@ void vcpu_show_registers(const struct vcpu *);
 unsigned long pv_guest_cr4_fixup(unsigned long guest_cr4);
 
 /* Convert between guest-visible and real CR4 values. */
-#define pv_guest_cr4_to_real_cr4(c) \
-    (((c) | (mmu_cr4_features & (X86_CR4_PGE | X86_CR4_PSE))) & ~X86_CR4_DE)
+#define pv_guest_cr4_to_real_cr4(v)                         \
+    (((v)->arch.guest_context.ctrlreg[4]                    \
+      | (mmu_cr4_features & (X86_CR4_PGE | X86_CR4_PSE))    \
+      | ((v)->domain->arch.vtsc ? X86_CR4_TSD : 0))         \
+      & ~X86_CR4_DE)
 #define real_cr4_to_pv_guest_cr4(c) \
-    ((c) & ~(X86_CR4_PGE | X86_CR4_PSE))
+    ((c) & ~(X86_CR4_PGE | X86_CR4_PSE | X86_CR4_TSD))
 
 void domain_cpuid(struct domain *d,
                   unsigned int  input,
index e533f1bee8898af5d8f228365309f2f7b1ab268c..cfacb04e4e3e5e2e9bbc4cbfe412c6b15df15449 100644 (file)
@@ -126,7 +126,6 @@ int hvm_do_hypercall(struct cpu_user_regs *pregs);
 void hvm_hlt(unsigned long rflags);
 void hvm_triple_fault(void);
 
-extern int opt_softtsc;
 void hvm_rdtsc_intercept(struct cpu_user_regs *regs);
 
 /* These functions all return X86EMUL return codes. */
index 1d687a8060a8dcd5867ca31efa279e19255b7bce..ec06313f952be64be6b07bb755357b58419fdc88 100644 (file)
@@ -41,4 +41,7 @@ int pit_broadcast_is_available(void);
 uint64_t acpi_pm_tick_to_ns(uint64_t ticks);
 uint64_t ns_to_acpi_pm_tick(uint64_t ns);
 
+extern int opt_softtsc;
+void pv_soft_rdtsc(struct vcpu *v, struct cpu_user_regs *regs);
+
 #endif /* __X86_TIME_H__ */